package com.ddyx.service.impl;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSONObject;
import com.ddyx.common.SimpleHttpPost;
import com.ddyx.common.util.MD5FileUtil;
import com.ddyx.entity.CaptureEvent;
import com.ddyx.entity.Customer;
import com.ddyx.entity.Device;
import com.ddyx.entity.EventLog;
import com.ddyx.entity.EventStorelog;
import com.ddyx.entity.FaceNotice;
import com.ddyx.entity.Store;
import com.ddyx.mapper.CaptureEventMapper;
import com.ddyx.mapper.CustomerMapper;
import com.ddyx.mapper.DeviceMapper;
import com.ddyx.mapper.EventLogMapper;
import com.ddyx.mapper.EventStorelogMapper;
import com.ddyx.mapper.FaceNoticeMapper;
import com.ddyx.mapper.ShopMapper;
import com.ddyx.mapper.StoreMapper;
import com.ddyx.model.DetectFaceInfo;
import com.ddyx.model.DeviceEventData;
import com.ddyx.model.FaceDetectResult;
import com.ddyx.model.FaceIdentifyResult;
import com.ddyx.model.FaceImageInfo;
import com.ddyx.model.IdentifyFaceInfo;
import com.ddyx.service.EventDataHandleService;
import com.ddyx.service.IDeviceEventDataParser;
import com.ddyx.service.IFaceRecognitionManager;
import com.ddyx.service.IImageStorageService;
import com.ddyx.service.IMessageQueueService;

/**
 * 赛蓝抓拍事件处理实现
 * 
 * @author zhongchzh
 *
 */
@Service("cylanEventDataHandleService")
public class CylanEventDataHandleServiceImpl implements EventDataHandleService {

	private static final Logger logger = LoggerFactory.getLogger(CylanEventDataHandleServiceImpl.class);
	
	public static final DateFormat dateformat_ymdh = new SimpleDateFormat("yyyy-MM-dd-HH");
	public static final DateFormat dateformat_ymdhms = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	
	private static String url = "http://api.retail.vip/vip-pushservice/push/pushNotice";
	
	@Resource
	private StoreMapper storeMapper;
	
	@Resource
	private ShopMapper shopMapper;
	
	@Resource
	private DeviceMapper deviceMapper;
	
	@Resource
	private CaptureEventMapper captureEventMapper;
	
	@Resource
	private CustomerMapper customerMapper;
	
	@Resource
	private EventLogMapper eventLogMapper;
	
	@Resource
	private EventStorelogMapper eventStorelogMapper;
	
	@Resource
	private FaceNoticeMapper faceNoticeMapper;
	
	@Resource(name="deviceEventDataParser")
	private IDeviceEventDataParser deviceEventDataParser;
	
	@Resource(name="faceRecognitionManager")
	private IFaceRecognitionManager faceRecognitionManager;
	
	@Resource(name="imageStorageService")
	private IImageStorageService imageStorageService;
	
	//@Resource(name="messageQueueService")
	//private IMessageQueueService messageQueueService;
	
	/**使用第三方算法接口*/
	private boolean userThirdpartyAPI = true;
	
	private static final String PRODUCE_TOPIC = "dingdangvip_test";
	
	@Override
	public JSONObject handle(JSONObject eventData) {
		JSONObject success = new JSONObject();
		success.put("status", "success");
		JSONObject failed = new JSONObject();
		failed.put("status", "failed");
		try {
			 //1、解析摄像头或者第三方云平台发送过来的数据
			logger.info("开始解析事件数据......");
			DeviceEventData deviceEventData = deviceEventDataParser.parse(eventData);
			if(deviceEventData == null) return failed;
			logger.info("事件数据解析完毕。数据 = {}", deviceEventData.toString());
			logger.info("图片数据接收时间：{}", (deviceEventData.getReceivedTime().getTime()-deviceEventData.getCapturedTime().getTime()));
			
			//2、验证设备是否绑定
			logger.info("开始验证设备合法性......");
			Device deviceExample = new Device();
			deviceExample.setDeviceName(deviceEventData.getDeviceNo());
			Device device = deviceMapper.selectOne(deviceExample);
			if (device == null) {
				logger.info("无法找到设备信息，该设备可能没有入库。");
				return failed;
			}
			
			Store storeExample = new Store();
			storeExample.setStoreId(device.getStoreId());
			Store store = storeMapper.selectOne(storeExample);
			if (store == null) {
				logger.info("无法找到店铺信息。");
				return failed;
			}
			logger.info("验证设备合法性成功。设备编码 = {}", device.getDeviceName());
			
			//3、调用第三方人脸检测接口检测人脸数据
			logger.info("开始检测人脸......");
			if(userThirdpartyAPI) {
				//调用第三方算法检测人脸
				for(int i=0;i<deviceEventData.getFaceImages().size();i++) {
					FaceImageInfo faceImageInfo = deviceEventData.getFaceImages().get(i);
					long start = System.currentTimeMillis();
					FaceDetectResult subDetectResult = faceRecognitionManager.detectFace(faceImageInfo.getFaceImageData());
					long end = System.currentTimeMillis();
					logger.info("调用第三方人脸检测接口花费时间：{}", (end-start));
					if(subDetectResult != null && subDetectResult.getResultSize() == 1) {
						DetectFaceInfo detectFaceInfo = subDetectResult.getResultList().get(0);
						faceImageInfo.setDetectFaceInfo(detectFaceInfo);
					}
				}
			} else {
				//直接赛蓝使用提供的人脸图和信息
			}
			logger.info("人脸检测完毕。");
			
			//把图片存储在自己的图片服务器上
			long start1 = System.currentTimeMillis();
			saveImageToMyServer(deviceEventData, store.getStoreNo());
			long end1 = System.currentTimeMillis();
			logger.info("调用第三方图片存储接口花费时间：{}", (end1-start1));
			
			//拿当前人脸去店铺的VIP 顾客人脸库和店员人脸库使用第三方的人脸识别算法进行人脸识别。
			for(int i=0;i<deviceEventData.getFaceImages().size();i++) {
				FaceImageInfo faceImageInfo = deviceEventData.getFaceImages().get(i);
				if(faceImageInfo.getDetectFaceInfo() == null) continue;
				
				// 创建抓拍事件数据
				CaptureEvent captureEvent = createCaptureEventData(deviceEventData, faceImageInfo, device, store);
				
				//在VIP顾客人脸库和店员人脸库中识别人脸
				long start = System.currentTimeMillis();
				IdentifyFaceInfo identifyFaceInfo = identifyFaceForVipAndEmp(faceImageInfo, store);
				long end = System.currentTimeMillis();
				logger.info("调用第三方人脸识别接口花费时间：{}", (end-start));
				if(identifyFaceInfo != null) {
					captureEvent.setContactId(identifyFaceInfo.getUserId());
					captureEvent.setPersonId(identifyFaceInfo.getUserId());
					
					Customer vip = isVipCustomer(captureEvent, store);
					if(vip != null) {
						//如果是店员，则直接放弃后面的处理。
						if(StringUtils.hasText(vip.getTags()) && vip.getTags().contains("店员")) return success;
						
						//发送VIP进店通知
						pushNotice(captureEvent, vip);
						
						//更新VIP顾客最后到店事件和到店次数
						updateVipCustomer(vip.getCustomerId(), captureEvent.getCaptureAt());
						
						//保存提醒记录
						saveNotice(captureEvent, store);
					}
				} else {
					//写入消息队列，采用异步处理非VIP的抓拍事件
					//messageQueueService.pubish(PRODUCE_TOPIC, "This is Test mseeage.");
					
					//识别顾客人脸
					IdentifyFaceInfo identifyFaceInfoForCust = identifyFaceForCustomer(faceImageInfo, store);
					if(identifyFaceInfoForCust != null) {
						captureEvent.setContactId(identifyFaceInfoForCust.getUserId());
						captureEvent.setPersonId(identifyFaceInfoForCust.getUserId());
					} else {
						addFaceForCustomer(captureEvent, faceImageInfo, store);
					}
				}
				
				start = System.currentTimeMillis();
				//保存抓拍事件
				saveCaptureEventData(captureEvent, identifyFaceInfo, store);
				
				//保存店铺事件
				saveStoreEventLog(captureEvent, store);
				
				//更新设备最后数据接收时间
				deviceMapper.updateLatestReceivedTime(deviceEventData.getDeviceNo(), captureEvent.getCreatedAt());
				end = System.currentTimeMillis();
				logger.info("保存事件到数据库花费时间：{}", (end-start));
			}
			logger.info("处理事件花费时间：{}", (System.currentTimeMillis()-deviceEventData.getReceivedTime().getTime()));
			return success;
		} catch(Exception err) {
			logger.error("处理抓拍事件错误：", err);
			failed.put("status", "error");
			return failed;
		}
	}
	
	private void saveImageToMyServer(DeviceEventData deviceEventData, String storeNo) {
		//存储底图
		String originalImgUrl = deviceEventData.getOriginalImageUrl();
		String baseName = String.valueOf(System.currentTimeMillis());
		String originalImageName = baseName + "_full.jpg";
		String imagePath = genImageStorageUrl(deviceEventData.getOriginalImageData(), storeNo, deviceEventData.getDeviceNo(), originalImageName);
		imagePath = imageStorageService.save("cusface", imagePath, deviceEventData.getOriginalImageData());
		deviceEventData.setOriginalImageUrl(imagePath);
		
		//存储脸图
		for(int i=0;i<deviceEventData.getFaceImages().size();i++) {
			FaceImageInfo faceImageInfo = deviceEventData.getFaceImages().get(i);
			//判断底图和人脸图是否为同一张图片。如果是同一张，防止多次保存。
			if(StringUtils.hasText(originalImgUrl) && StringUtils.hasText(faceImageInfo.getFaceImageUrl()) && originalImgUrl.equals(faceImageInfo.getFaceImageUrl())) {
				faceImageInfo.setFaceImageUrl(deviceEventData.getOriginalImageUrl());
			} else {
				String faceImageName = baseName + "_s" + i + ".jpg";
				imagePath = genImageStorageUrl(faceImageInfo.getFaceImageData(), storeNo, deviceEventData.getDeviceNo(), faceImageName);
				imagePath = imageStorageService.save("cusface", imagePath, faceImageInfo.getFaceImageData());
				faceImageInfo.setFaceImageUrl(imagePath);
			}
		}
	}
	
	/**
	 * 生成图片存储地址
	 * @param storeNo
	 * @param imageData
	 * @return
	 */
	private String genImageStorageUrl(byte[] imageData, String storeNo, String deviceNo, String imageName) {
		StringBuilder sb = new StringBuilder();
		sb.append(storeNo);
		sb.append("/");
		sb.append(deviceNo);
		sb.append("/");
		String ymdh = dateformat_ymdh.format(new Date());
		String[] s = ymdh.split("-");
		for(int i=0;i<s.length;i++) {
			sb.append(s[i]);
			sb.append("/");
		}
		//sb.append(MD5FileUtil.getMD5String(imageData));
		//sb.append(".jpg");
		sb.append(imageName);
		return sb.toString();
	}
	
	/**
	 * 保存原始事件数据
	 * @param requestData
	 * @return
	 */
	private CaptureEvent createCaptureEventData(DeviceEventData deviceEventData, FaceImageInfo faceImageInfo, Device device, Store store) {
		CaptureEvent captureEvent = new CaptureEvent();
		
		DetectFaceInfo detectFaceInfo = faceImageInfo.getDetectFaceInfo();
		
		//店铺信息
		captureEvent.setShopId(store.getStoreId());
		captureEvent.setStoreNo(store.getStoreNo());
		
		//设备信息
		captureEvent.setDeviceId(device.getDeviceId());
		captureEvent.setDeviceName(device.getDeviceName());
		
		//人脸识别信息
		captureEvent.setDetectFace(faceImageInfo.getFaceImageUrl());
		//用于标示人脸
		captureEvent.setFaceToken(UUID.randomUUID().toString().replaceAll("-", ""));
		
		captureEvent.setDetectGender(String.valueOf(detectFaceInfo.getGender()));
		captureEvent.setDetectAge(detectFaceInfo.getAge());
		//captureEvent.setConfidence((double)faceInfo.getFaceProbability());
		//事件类型（进店 in）
		captureEvent.setEventType("in");
		//图片角度（analyzed, bad_yaw_face, bad_roll_face, bad_pitch_face）
		if(detectFaceInfo.getPitchAngle() < -20 || detectFaceInfo.getPitchAngle() > 20) {
			captureEvent.setStatus("bad_pitch_face");
		} else if(detectFaceInfo.getRollAngle() < -20 || detectFaceInfo.getRollAngle() > 20) {
			captureEvent.setStatus("bad_roll_face");
		} else if(detectFaceInfo.getYawAngle() < -20 || detectFaceInfo.getYawAngle() > 20) {
			captureEvent.setStatus("bad_yaw_face");
		} else {
			captureEvent.setStatus("analyzed");
		}
		
		captureEvent.setEventsCount(1);
		
		//其他信息
		captureEvent.setCaptureAt(deviceEventData.getCapturedTime());
		captureEvent.setCreatedAt(deviceEventData.getReceivedTime());
		captureEvent.setCreateTime(new Date());
		
		return captureEvent;
	}
	
	private void saveCaptureEventData(CaptureEvent captureEvent, IdentifyFaceInfo identifyFaceInfo, Store store) {
		if(identifyFaceInfo != null) {
			captureEvent.setSearchResult("true");
			captureEvent.setConfidence(identifyFaceInfo.getScore());
		}
		captureEventMapper.insert(captureEvent);
	}
	
	/**
	 * 识别人脸
	 * 
	 * @param faceInfo
	 * @param store
	 * @return
	 */
	private IdentifyFaceInfo identifyFaceForVipAndEmp(FaceImageInfo faceImageInfo, Store store) {
		StringBuilder sb = new StringBuilder();
		//VIP顾客人脸库
		sb.append(store.getStoreNo());
		sb.append("_VIP");
		sb.append(",");
		//店员人脸库
		sb.append(store.getStoreNo());
		sb.append("_EMP");
		FaceIdentifyResult identifyResult = faceRecognitionManager.identifyFace(faceImageInfo.getFaceImageData(), sb.toString());
		if(identifyResult == null || identifyResult.getResultSize() == 0) return null;
		return identifyResult.getResultList().get(0);
	}
	
	private IdentifyFaceInfo identifyFaceForCustomer(FaceImageInfo faceImageInfo, Store store) {
		StringBuilder sb = new StringBuilder();
		//所有顾客人脸库
		sb.append(store.getStoreNo());
		sb.append("_CUSTOMER");
		FaceIdentifyResult identifyResult = faceRecognitionManager.identifyFace(faceImageInfo.getFaceImageData(), sb.toString());
		if(identifyResult == null || identifyResult.getResultSize() == 0) return null;
		return identifyResult.getResultList().get(0);
	}
	
	/**
	 * 将顾客人脸加入到人脸库
	 * @param captureEvent
	 * @param faceImageInfo
	 * @param store
	 */
	private void addFaceForCustomer(CaptureEvent captureEvent, FaceImageInfo faceImageInfo, Store store) {
		String contactId = UUID.randomUUID().toString().replaceAll("-", "");
		StringBuilder groupIdSb = new StringBuilder();
		//所有顾客人脸库
		groupIdSb.append(store.getStoreNo());
		groupIdSb.append("_CUSTOMER");
		faceRecognitionManager.addFace(contactId, "UNKNOWN", groupIdSb.toString(), faceImageInfo.getFaceImageData(), false);
		captureEvent.setContactId(contactId);
		captureEvent.setPersonId(contactId);
	}
	
	/**
	 * 识别是否为店铺VIP
	 * @return
	 */
	private Customer isVipCustomer(CaptureEvent captureEvent, Store store) {	
		Customer example = new Customer();
		example.setContactId(captureEvent.getContactId());
		example.setStoreNo(store.getStoreNo());
		example.setIsvip(1);
		List<Customer> resultList = customerMapper.select(example);
		
		if(!CollectionUtils.isEmpty(resultList)) {
			Customer result = resultList.get(0);
			captureEvent.setCustomerId(result.getCustomerId());
			return result;
		}
		return null;
	}
	
	private void pushNotice(CaptureEvent captureEvent, Customer vip) {
		try {
			if (vip != null) {
				JSONObject notice = new JSONObject();
				HashMap<String, Object> map = new HashMap<String, Object>();
				map.put("storeNo", vip.getStoreNo());
				map.put("customerId", vip.getCustomerId());
				map.put("customerName", vip.getCustomerName());
				map.put("nickname", vip.getNickname());
				map.put("headImage", captureEvent.getDetectFace());
				map.put("gender", vip.getGender());
				map.put("contactId", vip.getContactId());

				map.put("snapTime", vip.getSnapTime());
				map.put("lastDateArrived", vip.getLastDateArrived());
				map.put("totalTimes", vip.getTotalTimes());
				map.put("totalAmount", vip.getTotalAmount());
				map.put("colorName", vip.getColorName());
				map.put("sizeName", vip.getSizeName());
				map.put("styleName", vip.getStyleName());
				map.put("tags", vip.getTags());

				notice.put("data", map);
				SimpleHttpPost.getInstance().postJson(url, notice);
			}
		} catch (Exception err) {
			logger.error("发送VIP进店通知失败：", err);
		}
	}
	
	private void updateVipCustomer(Integer customerId, Date captureAt) {
		String lastDateArrived = dateformat_ymdhms.format(captureAt);
		Customer oldCustomer = customerMapper.selectByPrimaryKey(customerId);
		// 更新VIP到店次数和最近到店时间
		Customer customer = new Customer();
		customer.setCustomerId(customerId);
		customer.setLastDateArrived(lastDateArrived);
		int times = 0;
		if(oldCustomer.getTotalTimes() != null) {
			times = oldCustomer.getTotalTimes();
		}
		customer.setTotalTimes(times);
		customerMapper.updateLastArrivedInfo(customer);
	}
	
	private void saveNotice(CaptureEvent captureEvent, Store store) {
		FaceNotice faceNotice = new FaceNotice();
		faceNotice.setContactId(captureEvent.getContactId());
		faceNotice.setImageUuid(captureEvent.getDetectFace());
		Date noticeDatetime = new Date();
		faceNotice.setMsec(noticeDatetime.getTime());
		faceNotice.setNoticeDatetime(noticeDatetime);
		faceNotice.setNoticeTitle(store.getStoreName());
		faceNotice.setStoreId(store.getStoreId());
		faceNoticeMapper.insert(faceNotice);
	}
	
	private void saveStoreEventLog(CaptureEvent captureEvent, Store store) {
		String contactId = captureEvent.getContactId();
		String headImage = captureEvent.getDetectFace();
		String snapTime = dateformat_ymdhms.format(captureEvent.getCaptureAt());
		// 写事件表
		EventLog eventLog = new EventLog();
		eventLog.setContactId(contactId);
		// 1:进店,2:购买
		eventLog.setEventType("1");
		eventLog.setEventTimestamp(snapTime);
		eventLogMapper.insert(eventLog);

		if (null != eventLog.getEventId()) {
			EventStorelog storeLog = new EventStorelog();
			storeLog.setContactId(contactId);
			storeLog.setEventId(eventLog.getEventId());
			storeLog.setHeadImage(headImage);
			storeLog.setSnapTime(snapTime);
			storeLog.setStoreName(store.getStoreName());
			eventStorelogMapper.insert(storeLog);
		}
	}
}
